/* tabmap.c */ 

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "tabmap.h"

void check_full(tabmap_t * map);

tabmap_t * tabmap_new(){
	tabmap_t *tmap = NULL;
	int *map = NULL;

	if((tmap = (tabmap_t *)malloc(sizeof(tabmap_t))) == NULL){
		fputs("Out of memory while allocating tabmap!", stderr);
		exit(1);
	}
	if((map = (int *)malloc(MAP_CAP * sizeof(int))) == NULL){
		fputs("Out of memory while allocating tabmap!", stderr);
		exit(1);
	}
	tmap->map = map;
	map[0] = 0;
	tmap->cap = MAP_CAP;
	tmap->len = 1;
	return tmap;
}

tabmap_t * tabmap_get_tabmap(char * str){
	int c_count = 0;
	int i;
	tabmap_t *newmap = tabmap_new();
	newmap->len = 0;
	for(i=0;i<strlen(str);i++){
		if(str[i] == '\t'){
			newmap->map[newmap->len ++]= c_count;
			check_full(newmap);
			c_count = 0;
		}else{
			c_count++;
		}
	}
	if(str[i-1] == '\t'){
		newmap->map[newmap->len ++]= 0;
		check_full(newmap);
	}else{
		newmap->map[newmap->len ++] = c_count;
		check_full(newmap);
	}
	return newmap;
}

int tabmap_get_actual_offset(tabmap_t *tmap, int logical_offset){
	int offset = 0;
	int i;
	int *map = tmap->map;
	for(i=0;i<tmap->len;i++){
		if(logical_offset - map[i] <= 0){
			offset += logical_offset;
			return offset;
		}else{
			offset += ((map[i]/TAB_WIDTH)+1)*TAB_WIDTH;
			logical_offset -= (map[i]+1);
		}
	}
	return offset;
}

int tabmap_get_actual_len(tabmap_t *tmap){
	int count = 0;
	int i;
	int *map = tmap->map;
	for(i=0; i<tmap->len-1; i++){
		count += ((map[i]/TAB_WIDTH)+1)*TAB_WIDTH;
	}
	count += map[i];
	return count;
}

int tabmap_get_logical_offset(tabmap_t *tmap, int actual_offset){
	int logical_offset = 0;
	int i;
	int *map = tmap->map;
	for(i = 0; i <tmap->len; i++){
		int ac_len = (map[i]/TAB_WIDTH + 1)*TAB_WIDTH;
		if(actual_offset - map[i] < 0){
			return (logical_offset + actual_offset);
		}else if(actual_offset - ac_len < 0){
			return (logical_offset + map[i]);
		}else{
			actual_offset -= ac_len;
			logical_offset += map[i]+1;
		}
	}
	return logical_offset;
}

int tabmap_get_logical_shift(tabmap_t *tmap, int logical_offset, int actual_shift){
	int actual_offset = tabmap_get_actual_offset(tmap, logical_offset) + actual_shift;
	int logical_shift = logical_offset - tabmap_get_logical_offset(tmap, actual_offset);
	return logical_shift;
}


int tabmap_insert_tab(tabmap_t *tmap, int logical_offset){
	int i;
	int *map = tmap->map;
	for(i=0;i<tmap->len;i++){
		if(logical_offset - map[i] <= 0){
			//insert an entry behind i;
			int j;
			for(j = tmap->len -1; j >= i+1; j--){
				map[j+1] =map[j];
			}
			map[i+1] = map[i] - logical_offset;
			map[i] = logical_offset;
			tmap->len ++;
			check_full(tmap);
			return 0;
		}else{
			logical_offset -= (map[i]+1);
	
		}
	}
	map[tmap->len++]=0;
	check_full(tmap);
	return 0;
}

int tabmap_insert_char(tabmap_t *tmap, int logical_offset,int ch){
	if(ch == '\t'){
		return tabmap_insert_tab(tmap, logical_offset);
	}else{
		int i;
		int *map = tmap->map;
		for(i=0;i<tmap->len;i++){
			if(logical_offset - map[i] <= 0){
				map[i]++;
				return 0;
			}else{
				logical_offset -= (map[i]+1);
		
			}
		}
		map[tmap->len -1]++;
	}
	return 0;
}




int tabmap_delete_char(tabmap_t *tmap, int logical_offset){
	int i;
	int *map = tmap->map;
	for(i=0;i<tmap->len;i++){
		if(logical_offset - map[i] < 0){	//find the position
			//decrease the entry
			map[i]--;
			return 0;
		}else if (logical_offset - map[i] == 0){ //it is a tab
			//merge two entries
			int j;
			map[i] += map[i+1];
			for(j=i+2; j<tmap->len; j++){
				map[j-1] = map[j];
			}
			tmap->len --;
			return 0;
		}else{
			logical_offset -= (map[i]+1);
	
		}
	}
	return 0;
}


int tabmap_insert_str(tabmap_t *tmap, int pos, char *str){
	int i = 0;
	while(str[i] != '\0'){
		tabmap_insert_char(tmap, pos++, str[i++]);
	}
	return 0;
}

int tabmap_delete_str(tabmap_t *tmap, int from, int len){
	int i=0;
	for(i=0; i < len; i++){
		tabmap_delete_char(tmap, from);
	}
	return 0;
}


void check_full(tabmap_t * map){
	int *data;
	if(map->len < map->cap){
		return;
	}
	if((data = (int *)realloc(map->map,map->cap * 2 * sizeof(int))) == NULL){
		fputs("Out of memory while allocating tabmap!", stderr);
		exit(1);
	}
	map->cap *=2;
	map->map = data;
}

tabmap_t *tabmap_copy_tabmap(tabmap_t *tmap){
	tabmap_t *map = NULL;
	int * data = NULL;
	int i;
	map = tabmap_new();
	if(map == NULL){
		fputs("Out of memory while allocating tabmap!", stderr);
		exit(1);
	}
	if((data = (int *)malloc(tmap->cap * sizeof(int))) == NULL){
		fputs("Out of memory while allocating tabmap!", stderr);
		exit(1);
	}
	for(i=0;i<tmap->len;i++){
		data[i] = tmap->map[i];
	}
	free(map->map);
	map->map = data;
	return map;
}
